/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.tnmk.worktime.service.dao;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import org.tnmk.worktime.exception.NotFoundTableException;
import org.tnmk.worktime.service.dao.entry.BaseEntry;

/**
 *
 * @author tnmkhoi
 * @param <E> extends from BaseEntry, which has attribute 'id' and serializable (for storing to file)
 */
public abstract class StorageBuffer {

  private static final Logger LOG = Logger.getLogger(StorageBuffer.class);
  private Map<Class,DataTable> storage = new HashMap<>();
  
  public void init(){
    List<Class> entryClasses = getEntryClasses();
    for (Class clazz : entryClasses) {
      storage.put(clazz, new DataTable());
    }
    
    storage = this.initStorage();
  }
  public abstract List<Class> getEntryClasses();
  
  public <E extends BaseEntry> E delete(Class<E> clazz, Integer id){
    E e = find(clazz, id);
    return delete(e);
  }

  public <E extends BaseEntry> E delete(E e){
    if (e == null) {
      return null;
    }
    DataTable<E> tbl = getTable(e.getClass());
    return tbl.remove(e.getId());    
  }

  /**
   * @param clazz
   * @return Must find a corresponding table. If not found, throw Exception.
   */
  protected DataTable getTable(Class clazz){
    DataTable tbl = storage.get(clazz);
    if (tbl == null){
      Set<Class> tableClasses = storage.keySet();
      for (Class itblClass : tableClasses) {
        if (itblClass.isAssignableFrom(clazz)){
          return storage.get(itblClass);
        }
      }
      //Not found, throw exception
      throw new NotFoundTableException(clazz, storage);
    }
    return tbl;
  }
  /**
   * @param e
   * @return If not found e in this#all, add to that list. Otherwise, replace the entry with the same id.
   */
  private <E extends BaseEntry> boolean replaceOrAdd(E e) {
    DataTable<E> tbl = getTable(e.getClass());
    tbl.set(e);
    return true;
  }  
  public <E extends BaseEntry> boolean addOrUpdate(E e) {
    return replaceOrAdd(e);
  }

  public <E extends BaseEntry> boolean addOrUpdate(List<E> list) {
    for (E e : list) {
      replaceOrAdd(e);
    }
    return true;
  }

  /**
   * @param list this list will replace the current data
   * @return
   */
  public abstract boolean save();
  public abstract void backupData();
  public <E extends BaseEntry> E find(Class<E> clazz, Integer id) {
    DataTable<E> tbl = getTable(clazz);
    return tbl.select(id);
  }
  /**
   * Simulate select *
   * @return 
   */
  public <E extends BaseEntry> List<E> findAll(Class<E> clazz) {
    DataTable<E> tbl = getTable(clazz);
    return tbl.selectAll();
  }

  protected abstract Map<Class, DataTable> initStorage();

  protected Map<Class,DataTable> getStorage() {
    return storage;
  }
}
